package com.lyf.alg.practice.devlop;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @AUTHOR LYF
 * @DATE 2022/5/30
 * @VERSION 1.0
 * @DESC
 * 服务熔断：流量限流算法
 * （1）漏桶算法 (理解流速和桶容量，capacity并发量，outRate--QPS）
 * （2）令牌算法
 */
// 100的并发量时，QPS为10
// 一个人时则需要达到每秒10次并请求100次才行

class BucketAlg{
    //桶的容量 - 同一时刻的并发量
    private int capacity = 100;
    //剩余请求量
    // 可以进行放请求--队列--消息请求队列
    private AtomicInteger water = new AtomicInteger(0);
    //流控速率,每秒钟的请求数
    private int outRate=10;
    //
    private long lastRequireTime;

    public AtomicInteger getWater() {
        return water;
    }

    public boolean acquire(){
        //桶是空的
        if(water.get()==0){
            lastRequireTime=System.currentTimeMillis();
            water.addAndGet(1);
            return true;
        }

//        long curTime=System.currentTimeMillis();
//        //long qps = ;
//
//        long qps = (water.get()* 1000L) / (curTime-lastRequireTime);

        // 先计算下上成功接受到当前时间已经流出的记录数
        // 按照流速正常可以流出的数量---outNum
        int outNum = ((int) ((System.currentTimeMillis() - lastRequireTime) / 1000)) * outRate;
        int waterLeft = water.get() - outNum;// 比较现有的桶的水量和正常流出的

        water.set(Math.max(0, waterLeft));// 若正常的流出多则是为0，处理完毕

        // 重新更新leakTimeStamp
        // outNum是否大于0---cuiyaonan2000@163.com
        if (outNum > 0) {
            lastRequireTime = System.currentTimeMillis();// 只要有流出则有变化
        }

        if(water.get()<capacity){
            //大于-
            water.addAndGet(1);
//            lastRequireTime=System.currentTimeMillis();
            return true;
        }else{
            //water.getAndAdd(1);
            return false;
        }

    }

}
class TokenAlg{


}

public class LimitFlowAlg {

    public static void main(String[] args) {
        // 1. 模拟单用户
        BucketAlg bucketAlg=new BucketAlg();
//        for(int i=0;i<10000;i++){
//            boolean flag = bucketAlg.acquire();
//            System.out.println(i+":>>>"+flag+":"+bucketAlg.getWater().get());
//        }
//
        // 2. 模拟100用户

        for(int i=0;i<100;i++){
            int finalI = i;
            new Thread(()->{
                for(int j=0;j<20;j++){
                    boolean flag = bucketAlg.acquire();
                    System.out.println("用户"+finalI +":>>>"+j+"次请求>>"+flag+":"+bucketAlg.getWater().get());
                }
            }).start();
        }

    }


}
